package docker_agent

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net/url"
	"os"
	"strconv"
	"strings"

	devcontainer_dto "code.gitea.io/gitea/modules/devstar_devcontainer/k8s_agent/dto"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	devcontainer_service_dto "code.gitea.io/gitea/services/devstar_devcontainer/dto"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
	apimachinery_meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var commonSocketPaths = []string{
	"/var/run/docker.sock",
	"/run/podman/podman.sock",
	"$HOME/.colima/docker.sock",
	"$XDG_RUNTIME_DIR/docker.sock",
	"$XDG_RUNTIME_DIR/podman/podman.sock",
	`\\.\pipe\docker_engine`,
	"$HOME/.docker/run/docker.sock",
}

func AssignDevcontainerCreationDockerOperator(ctx *context.Context, newDevContainer *devcontainer_service_dto.CreateDevcontainerDTO) error {
	log.Info("Docker create container.....")
	// 1. 创建docker client
	cli, err := CreateDockerClient(ctx)
	defer cli.Close()
	if err != nil {
		return err
	}

	// 2. 创建容器
	opts := &devcontainer_dto.CreateDevcontainerOptions{
		Name:          newDevContainer.Name,
		CreateOptions: apimachinery_meta_v1.CreateOptions{},
		Image:         newDevContainer.Image,
		CommandList: []string{
			"sh", 
			"-c",
			"rm -f /etc/ssh/ssh_host_*; ssh-keygen -A ; service ssh start ; while true; do sleep 60; done",
		},
		SSHPublicKeyList: newDevContainer.SSHPublicKeyList,
		GitRepositoryURL: newDevContainer.GitRepositoryURL,
	}

	// 2. 创建成功，取回集群中的 DevContainer
	port, err := CreateDevcontainer(ctx, cli, opts)
	if err != nil {
		return err
	}
	// 3. 将分配的 NodePort Service 写回 newDevcontainer，供写入数据库进行下一步操作
	uint16Value, _ := strconv.ParseUint(port, 10, 16)
	newDevContainer.DevcontainerPort = uint16(uint16Value)
	return nil
}

func CreateDevcontainer(ctx *context.Context, cli *client.Client, opts *devcontainer_dto.CreateDevcontainerOptions) (string, error) {
	if ctx == nil || opts == nil {
		return "", fmt.Errorf("拉取镜像失败:环境为空")
	}
	// 拉取镜像
	err := pullImage(cli, opts.Image)
	if err != nil {
		return "", fmt.Errorf("拉取镜像失败:%v", err)
	}
	// 创建并启动容器
	port, err := createAndStartContainer(cli, opts)
	if err != nil {
		return "", fmt.Errorf("创建或启动容器失败:%v", err)
	}
	return port, nil
}

// pullImage 用于拉取指定的 Docker 镜像
func pullImage(cli *client.Client, image string) error {
	ctx := context.Background()
	resp, err := cli.ImagePull(ctx, image, types.ImagePullOptions{})
	if err != nil {
		return err
	}
	defer resp.Close()

	_, err = io.Copy(os.Stdout, resp)
	return err
}

func createAndStartContainer(cli *client.Client, opts *devcontainer_dto.CreateDevcontainerOptions) (string, error) {
	ctx := context.Background()

	// 创建容器配置
	config := &container.Config{
		Image:        opts.Image,
		Cmd:          opts.CommandList,
		AttachStdout: true,
		AttachStderr: true,
		Tty:          true,
		OpenStdin:    true,
		ExposedPorts: nat.PortSet{
			"22/tcp": struct{}{},
		},
	}

	// 设置容器主机配置
	hostConfig := &container.HostConfig{
		// PortBindings: nat.PortMap{
		// 	"22/tcp": []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: strconv.Itoa(int(opts.ServicePort))}}, // 将容器的22端口映射到宿主机的8080端口
		// },
		ExtraHosts:      []string{"host.docker.internal:" + setting.Devstar.Devcontainer.Host},
		PublishAllPorts: true,
	}

	// 创建容器
	resp, err := cli.ContainerCreate(ctx, config, hostConfig, nil, nil, opts.Name)
	if err != nil {
		return "", fmt.Errorf("创建或启动容器失败:%v", err)
	}

	// 启动容器
	err = cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
	if err != nil {
		return "", fmt.Errorf("创建或启动容器失败:%v", err)
	}
	// 将公钥数组转换为字符串
	keysString := strings.Join(opts.SSHPublicKeyList, "\n")
	log.Info(fmt.Sprintf("keysString %s", keysString))
	// 解析原始 URL
	parsedURL, err := url.Parse(opts.GitRepositoryURL)
	if err != nil {
		fmt.Printf("Error parsing URL: %v\n", err)
		return "", fmt.Errorf("error pull code from git: %v", err)
	}
	// 获取主机和端口
	hostParts := strings.Split(parsedURL.Host, ":")

	port := ""
	if len(hostParts) > 1 {
		port = hostParts[1]
	}
	// 修改主机部分
	newHost := "host.docker.internal"
	if port != "" {
		newHost += ":" + port
	}

	// 设置新的主机
	parsedURL.Host = newHost

	// 生成新的 URL
	newURL := parsedURL.String()
	cmd := []string{
		"sh", "-c",
		"if [ ! -d '/data/workspace' ]; then git clone " + newURL + " /data/workspace && echo \"Git Repository cloned.\"; else echo \"Folder already exists.\"; fi;  mkdir -p ~/test; mkdir -p ~/.ssh ; chmod 700 ~/.ssh; echo \"" + keysString + "\" > ~/.ssh/authorized_keys ; chmod 600 ~/.ssh/authorized_keys; ",
	}
	// 创建 exec 实例
	ex, err := cli.ContainerExecCreate(context.Background(), resp.ID, types.ExecConfig{
		Cmd:          cmd,
		AttachStdout: true,
		AttachStderr: true,
	})
	if err != nil {
		return "", fmt.Errorf("启动执行器失败:%v", err)
	}
	execResp, err := cli.ContainerExecAttach(context.Background(), ex.ID, types.ExecStartCheck{})
	if err != nil {
		return "", fmt.Errorf("执行命令失败:%v", err)
	}
	output, err := ioutil.ReadAll(execResp.Reader)
	log.Info("Command output:\n%s\n", output)
	// 获取容器详细信息
	containerJSON, err := cli.ContainerInspect(context.Background(), resp.ID)
	if err != nil {
		return "", fmt.Errorf("获取容器信息失败:%v", err)
	}

	// 获取端口映射信息
	portBindings := containerJSON.NetworkSettings.Ports
	for containerPort, bindings := range portBindings {
		for _, binding := range bindings {
			log.Info("Container Port %s is mapped to Host Port %s on IP %s\n", containerPort, binding.HostPort, binding.HostIP)
			if containerPort.Port() == "22" {
				return binding.HostPort, nil
			}

		}
	}
	return "", fmt.Errorf("未找到映射的端口")
}
